package fulltextsearch

import (
	"gmap-backend/internal/config"
	"gmap-backend/internal/server/serviceimpl/fulltextsearch/searchlogic"
	"gmap-backend/internal/server/vo"
	"math"
	"runtime"
	"sync"
)

var searchLock sync.Mutex

// SearchAllTags 只查找所有标签
func SearchAllTags(appConfig *config.AppConfig, req *vo.BaseActionReq, bins [][]byte) *vo.TxtsResp {
	searchLock.Lock()
	defer searchLock.Unlock()
	searchlogic.ReloadCache(appConfig.PathConfig.MapsDir)
	return &vo.TxtsResp{
		Txts: searchlogic.GetAllTags(),
	}
}

// Search 全文搜索
func Search(appConfig *config.AppConfig, req *vo.TxtReq, bins [][]byte) *vo.FulltextSearchResp {
	// 表达式解析为各查询器
	searchers, hasUnclosedQuot := searchlogic.ParseSearchers(req.Txt)
	if nil == searchers || 0 == len(searchers) {
		return &vo.FulltextSearchResp{
			Items: []vo.SearchResult{},
			Extra: vo.SearchResultExtra{
				HasUnclosedQuot: false,
				PreciseTags:     []vo.PreciseTag{},
			},
		}
	}

	// 表达式中指定的要精确匹配的tag
	preciseTags := make([]vo.PreciseTag, 0, 2)
	for _, searcher := range searchers {
		if tagExpSearcher, ok := searcher.(*searchlogic.TagExpSearcher); ok {
			if !tagExpSearcher.Wild {
				preciseTags = append(preciseTags, vo.PreciseTag{
					Tag:     tagExpSearcher.Keyword,
					QuotCnt: tagExpSearcher.QuotCnt,
				})
			}
		}
	}

	// 缓存刷新
	searchLock.Lock()
	defer searchLock.Unlock()
	searchlogic.ReloadCache(appConfig.PathConfig.MapsDir)

	// 按逻辑cpu数量平均分配查询任务，等待所有任务完成后把查询结果合并
	mapsCnt := len(searchlogic.GetAllMaps())
	eachTaskMapsCnt := int(math.Max(float64(mapsCnt/runtime.NumCPU()+1), 10))
	var wg sync.WaitGroup

	taskFun := func(maps []searchlogic.MindMapInfo, searchers []searchlogic.ExpSearcher, retChan chan []vo.SearchResult) {
		defer wg.Done()
		retChan <- searchlogic.DoSearch(maps, searchers)
	}

	retsChans := make([]chan []vo.SearchResult, 0, 4)
	for i := 0; i < mapsCnt; i += eachTaskMapsCnt {
		wg.Add(1)
		eachRetChan := make(chan []vo.SearchResult, 1)
		retsChans = append(retsChans, eachRetChan)
		go taskFun(searchlogic.GetAllMaps()[i:int(math.Min(float64(i+eachTaskMapsCnt), float64(mapsCnt)))], searchers, eachRetChan)
	}

	wg.Wait()
	resultItems := make([]vo.SearchResult, 0, 4)
	for _, retChan := range retsChans {
		if eachItems := <-retChan; nil != eachItems && len(eachItems) > 0 {
			resultItems = append(resultItems, eachItems...)
		}
	}

	return &vo.FulltextSearchResp{
		Items: resultItems,
		Extra: vo.SearchResultExtra{
			HasUnclosedQuot: hasUnclosedQuot,
			PreciseTags:     preciseTags,
		},
	}
}
